package com.wzb.mybatisplus.bitaction;

import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class BlockCypherBatchQuery {

    private static final String BLOCKCYPHER_API_URL = "https://api.blockcypher.com/v1/btc/main/addrs";

    /**
     * 批量查询比特币地址余额（GET方式 - 适用于少量地址）
     */
    public static List<AddressBalance> batchGetBalance(List<String> addresses) {
        List<AddressBalance> results = new ArrayList<>();

        try {
            // 构建地址参数（用分号分隔）
            StringBuilder addressParam = new StringBuilder();
            for (String address : addresses) {
                if (addressParam.length() > 0) {
                    addressParam.append(";");
                }
                addressParam.append(address);
            }

            String url = BLOCKCYPHER_API_URL + "?limit=50&unspentOnly=true&includeScript=false&address=" + addressParam.toString();

            URL obj = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) obj.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("User-Agent", "Mozilla/5.0");
            conn.setRequestProperty("Accept", "application/json");

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                JSONObject jsonResponse = new JSONObject(response.toString());

                // 解析地址列表
                if (jsonResponse.has("addresses")) {
                    JSONArray addressesArray = jsonResponse.getJSONArray("addresses");
                    for (int i = 0; i < addressesArray.length(); i++) {
                        JSONObject addrData = addressesArray.getJSONObject(i);
                        AddressBalance balance = parseAddressData(addrData);
                        results.add(balance);
                    }
                }

            } else {
                System.out.println("请求失败，响应码: " + responseCode);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return results;
    }

    /**
     * 批量查询比特币地址详细信息（POST方式 - 推荐用于大量地址）
     */
    public static List<AddressBalance> batchPostBalance(List<String> addresses) {
        List<AddressBalance> results = new ArrayList<>();

        try {
            // BlockCypher 批量查询端点
            String url = "https://api.blockcypher.com/v1/btc/main/addrs?limit=50";

            // 构建请求体
            JSONObject requestBody = new JSONObject();
            JSONArray addressesArray = new JSONArray();

            for (String address : addresses) {
                addressesArray.put(address);
            }

            requestBody.put("addresses", addressesArray);

            URL obj = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) obj.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("User-Agent", "Mozilla/5.0");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Accept", "application/json");
            conn.setDoOutput(true);

            // 发送请求体
            try (OutputStream os = conn.getOutputStream()) {
                byte[] input = requestBody.toString().getBytes("utf-8");
                os.write(input, 0, input.length);
            }

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream(), "utf-8"));
                String inputLine;
                StringBuilder response = new StringBuilder();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                JSONObject jsonResponse = new JSONObject(response.toString());

                // 解析响应
                if (jsonResponse.has("addresses")) {
                    JSONArray addressesArrayResp = jsonResponse.getJSONArray("addresses");
                    for (int i = 0; i < addressesArrayResp.length(); i++) {
                        JSONObject addrData = addressesArrayResp.getJSONObject(i);
                        AddressBalance balance = parseAddressData(addrData);
                        results.add(balance);
                    }
                }

            } else {
                System.out.println("POST请求失败，响应码: " + responseCode);
                // 读取错误信息
                BufferedReader errorReader = new BufferedReader(
                        new InputStreamReader(conn.getErrorStream(), "utf-8"));
                String errorLine;
                StringBuilder errorResponse = new StringBuilder();
                while ((errorLine = errorReader.readLine()) != null) {
                    errorResponse.append(errorLine);
                }
                errorReader.close();
                System.out.println("错误信息: " + errorResponse.toString());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return results;
    }

    /**
     * 解析地址数据
     */
    private static AddressBalance parseAddressData(JSONObject addrData) {
        AddressBalance balance = new AddressBalance();

        if (addrData.has("address")) {
            balance.setAddress(addrData.getString("address"));
        }

        if (addrData.has("final_balance")) {
            long satoshis = addrData.getLong("final_balance");
            balance.setBalance(satoshis / 100000000.0);
            balance.setBalanceSatoshis(satoshis);
        }

        if (addrData.has("total_received")) {
            long received = addrData.getLong("total_received");
            balance.setTotalReceived(received / 100000000.0);
            balance.setTotalReceivedSatoshis(received);
        }

        if (addrData.has("total_sent")) {
            long sent = addrData.getLong("total_sent");
            balance.setTotalSent(sent / 100000000.0);
            balance.setTotalSentSatoshis(sent);
        }

        if (addrData.has("n_tx")) {
            balance.setTransactionCount(addrData.getInt("n_tx"));
        }

        if (addrData.has("unconfirmed_balance")) {
            long unconfirmed = addrData.getLong("unconfirmed_balance");
            balance.setUnconfirmedBalance(unconfirmed / 100000000.0);
            balance.setUnconfirmedBalanceSatoshis(unconfirmed);
        }

        if (addrData.has("final_n_tx")) {
            balance.setFinalTransactionCount(addrData.getInt("final_n_tx"));
        }

        if (addrData.has("unconfirmed_n_tx")) {
            balance.setUnconfirmedTransactionCount(addrData.getInt("unconfirmed_n_tx"));
        }

        return balance;
    }

    /**
     * 分批次批量查询（避免超过API限制）
     */
    public static List<AddressBalance> batchQueryWithChunking(List<String> addresses, int chunkSize) {
        List<AddressBalance> allResults = new ArrayList<>();

        for (int i = 0; i < addresses.size(); i += chunkSize) {
            int end = Math.min(i + chunkSize, addresses.size());
            List<String> chunk = addresses.subList(i, end);

            System.out.println("查询批次: " + (i/chunkSize + 1) + ", 地址数量: " + chunk.size());

            List<AddressBalance> chunkResults = batchPostBalance(chunk);
            allResults.addAll(chunkResults);

            // 添加延迟避免速率限制
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        return allResults;
    }

    public static void main(String[] args) {
        // 测试地址列表
        List<String> testAddresses = new ArrayList<>();
        testAddresses.add("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"); // 比特币创始地址
        testAddresses.add("1CounterpartyXXXXXXXXXXXXXXXUWLpVr");
        testAddresses.add("1FeexV6bAHb8ybZjqQMjJrcCrHGW9sb6uF");
        testAddresses.add("1HQ3Go3ggs8pFnXuHVHRytPCq5fGG8Hbhx");
        testAddresses.add("1JceMbs7o9HrR2o14xXKoK2xXWkyx3wUJp");

        System.out.println("=== 批量查询比特币地址余额 ===");

        // 方法1: GET方式批量查询
        System.out.println("\n1. GET方式批量查询:");
        List<AddressBalance> results1 = batchGetBalance(testAddresses);
        printResults(results1);

        // 方法2: POST方式批量查询
        System.out.println("\n2. POST方式批量查询:");
        List<AddressBalance> results2 = batchPostBalance(testAddresses);
        printResults(results2);

        // 方法3: 分批次查询（适用于大量地址）
        System.out.println("\n3. 分批次批量查询:");
        List<AddressBalance> results3 = batchQueryWithChunking(testAddresses, 3);
        printResults(results3);
    }

    private static void printResults(List<AddressBalance> results) {
        for (AddressBalance balance : results) {
            System.out.printf("地址: %s\n", balance.getAddress());
            System.out.printf("  余额: %.8f BTC (%d satoshis)\n",
                    balance.getBalance(), balance.getBalanceSatoshis());
            System.out.printf("  总接收: %.8f BTC\n", balance.getTotalReceived());
            System.out.printf("  总发送: %.8f BTC\n", balance.getTotalSent());
            System.out.printf("  交易数: %d\n", balance.getTransactionCount());
            if (balance.getUnconfirmedBalance() > 0) {
                System.out.printf("  未确认余额: %.8f BTC\n", balance.getUnconfirmedBalance());
            }
            System.out.println("---");
        }
    }

    /**
     * 地址余额信息类
     */
    public static class AddressBalance {
        private String address;
        private double balance;
        private long balanceSatoshis;
        private double totalReceived;
        private long totalReceivedSatoshis;
        private double totalSent;
        private long totalSentSatoshis;
        private int transactionCount;
        private double unconfirmedBalance;
        private long unconfirmedBalanceSatoshis;
        private int finalTransactionCount;
        private int unconfirmedTransactionCount;

        // Getters and Setters
        public String getAddress() { return address; }
        public void setAddress(String address) { this.address = address; }

        public double getBalance() { return balance; }
        public void setBalance(double balance) { this.balance = balance; }

        public long getBalanceSatoshis() { return balanceSatoshis; }
        public void setBalanceSatoshis(long balanceSatoshis) { this.balanceSatoshis = balanceSatoshis; }

        public double getTotalReceived() { return totalReceived; }
        public void setTotalReceived(double totalReceived) { this.totalReceived = totalReceived; }

        public long getTotalReceivedSatoshis() { return totalReceivedSatoshis; }
        public void setTotalReceivedSatoshis(long totalReceivedSatoshis) { this.totalReceivedSatoshis = totalReceivedSatoshis; }

        public double getTotalSent() { return totalSent; }
        public void setTotalSent(double totalSent) { this.totalSent = totalSent; }

        public long getTotalSentSatoshis() { return totalSentSatoshis; }
        public void setTotalSentSatoshis(long totalSentSatoshis) { this.totalSentSatoshis = totalSentSatoshis; }

        public int getTransactionCount() { return transactionCount; }
        public void setTransactionCount(int transactionCount) { this.transactionCount = transactionCount; }

        public double getUnconfirmedBalance() { return unconfirmedBalance; }
        public void setUnconfirmedBalance(double unconfirmedBalance) { this.unconfirmedBalance = unconfirmedBalance; }

        public long getUnconfirmedBalanceSatoshis() { return unconfirmedBalanceSatoshis; }
        public void setUnconfirmedBalanceSatoshis(long unconfirmedBalanceSatoshis) { this.unconfirmedBalanceSatoshis = unconfirmedBalanceSatoshis; }

        public int getFinalTransactionCount() { return finalTransactionCount; }
        public void setFinalTransactionCount(int finalTransactionCount) { this.finalTransactionCount = finalTransactionCount; }

        public int getUnconfirmedTransactionCount() { return unconfirmedTransactionCount; }
        public void setUnconfirmedTransactionCount(int unconfirmedTransactionCount) { this.unconfirmedTransactionCount = unconfirmedTransactionCount; }
    }
}